﻿#region  文件信息

// ---------------------------------------------------------------------------------------
// <copyright file="AliWapSub.cs" company="">
// 作者：李帅伦
// </copyright>
// <summary>
// 类型简介：
// 类型详细：AliWapSub.cs
// 创建时间：2015-01-07 22:30
// 修改时间：2015-01-07 18:20
// </summary>
// ---------------------------------------------------------------------------------------
#endregion

namespace Payment.Albaba.WapCode
{
    #region

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Web;
    using System.Xml;

    using Payment.Albaba.Com;

    #endregion

    /// <summary>
    ///     类名：Submit
    ///     功能：支付宝各接口请求提交类
    ///     详细：构造支付宝各接口表单HTML文本，获取远程HTTP数据
    ///     版本：3.3
    ///     修改日期：2011-07-05
    ///     说明：
    ///     以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
    ///     该代码仅供学习和研究支付宝接口使用，只是提供一个参考
    /// </summary>
    public class AliWapSubmit : PaySubBase
    {
        #region Fields

        /// <summary>
        ///     The config.
        /// </summary>
        private readonly AliConfig config;

        /// <summary>
        /// The format.
        /// </summary>
        private string format = "xml";

        // 返回格式 必填，不需要修改
        /// <summary>
        /// The v.
        /// </summary>
        private string v = "2.0";

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="AliWapSubmit"/> class.
        /// </summary>
        /// <param name="config">
        /// The config.
        /// </param>
        /// <param name="service">
        /// The service.
        /// </param>
        public AliWapSubmit(AliConfig config, AliWapService service)
        {
            this.Gateway = AliGateway.WapGatway;
            this.config = config;
           
            service.MerchantUrl = config.ErrorNotifyUrl;
            service.NotifyUrl = config.NotifyUrl;
            service.CallBackUrl = config.ReturnUrl;
            this.service = service;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the service.
        /// </summary>
        public AliWapService service { get; protected set; }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// 建立请求，以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
        /// </summary>
        /// <param name="gatewayNew">
        /// 支付宝网关地址
        /// </param>
        /// <param name="sParaTemp">
        /// 请求参数数组
        /// </param>
        /// <returns>
        /// 支付宝处理结果
        /// </returns>
        public string BuildRequest(string gatewayNew, Dictionary<string, string> sParaTemp)
        {
            Encoding code = Encoding.GetEncoding(this.config.InputCharset);

            // 待请求参数数组字符串
            string strRequestData = this.BuildRequestParaToString(sParaTemp, code);

            // 把数组转换成流中所需字节数组类型
            byte[] bytesRequestData = code.GetBytes(strRequestData);

            // 构造请求地址
            string strUrl = gatewayNew + "_input_charset=" + this.config.InputCharset;

            // 请求远程HTTP
            string strResult = string.Empty;
            try
            {
                // 设置HttpWebRequest基本信息
                HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(strUrl);
                myReq.Method = "post";
                myReq.ContentType = "application/x-www-form-urlencoded";

                // 填充POST数据
                myReq.ContentLength = bytesRequestData.Length;
                Stream requestStream = myReq.GetRequestStream();
                requestStream.Write(bytesRequestData, 0, bytesRequestData.Length);
                requestStream.Close();

                // 发送POST数据请求服务器
                HttpWebResponse HttpWResp = (HttpWebResponse)myReq.GetResponse();
                Stream myStream = HttpWResp.GetResponseStream();

                // 获取服务器返回信息
                StreamReader reader = new StreamReader(myStream, code);
                StringBuilder responseData = new StringBuilder();
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    responseData.Append(line);
                }

                // 释放
                myStream.Close();

                strResult = responseData.ToString();
            }
            catch (Exception exp)
            {
                strResult = "报错：" + exp.Message;
            }

            return strResult;
        }

        protected override string Method()
        {
            return "get";
        }
           

        /// <summary>
        /// 写入subdata到集合
        /// </summary>
        /// <returns></returns>
        protected override Dictionary<string, string> GetSubData()
        {
            // 请求号
            string reqId = DateTime.Now.ToString("yyyyMMddHHmmss");

            // 把请求参数打包成数组
            var sParaTempToken = new Dictionary<string, string>();
            sParaTempToken.Add("partner", this.config.Partner);
            sParaTempToken.Add("_input_charset", this.config.InputCharset.ToLower());
            sParaTempToken.Add("sec_id", this.config.SignType.ToUpper());
            sParaTempToken.Add("service", "alipay.wap.trade.create.direct");
            sParaTempToken.Add("format", this.format);
            sParaTempToken.Add("v", this.v);
            sParaTempToken.Add("req_id", reqId);
            sParaTempToken.Add("req_data", this.service.ReqDataToken);

            // 建立请求
            string sHtmlTextToken = this.BuildRequest(AliGateway.WapGatway, sParaTempToken);

            // URLDECODE返回的信息
            Encoding code = Encoding.GetEncoding(this.config.InputCharset);
            sHtmlTextToken = HttpUtility.UrlDecode(sHtmlTextToken, code);

            // 解析远程模拟提交后返回的信息
            Dictionary<string, string> dicHtmlTextToken = this.ParseResponse(sHtmlTextToken);
            // 获取token
                string requestToken = dicHtmlTextToken["request_token"];

                ////////////////////////////////////////////根据授权码token调用交易接口alipay.wap.auth.authAndExecute////////////////////////////////////////////
  try
            {
              
                // 业务详细
                var reqData = "<auth_and_execute_req><request_token>" + requestToken
                              + "</request_token></auth_and_execute_req>";

                // 把请求参数打包成数组
                var sParaTemp = new Dictionary<string, string>();
                sParaTemp.Add("partner", this.config.Partner);
                sParaTemp.Add("_input_charset", this.config.InputCharset.ToLower());
                sParaTemp.Add("sec_id", this.config.SignType.ToUpper());
                sParaTemp.Add("service", "alipay.wap.auth.authAndExecute");
                sParaTemp.Add("format", this.format);
                sParaTemp.Add("v", this.v);
                sParaTemp.Add("req_data", reqData);

                this.SubData = this.BuildRequestPara(sParaTemp);
                return this.SubData;
            }
            catch (Exception )
            {

                throw new Exception(requestToken);
            }
           
        }

        /// <summary>
        /// 解析远程模拟提交后返回的信息
        /// </summary>
        /// <param name="strText">
        /// 要解析的字符串
        /// </param>
        /// <returns>
        /// 解析结果
        /// </returns>
        public Dictionary<string, string> ParseResponse(string strText)
        {
            // 以“&”字符切割字符串
            string[] strSplitText = strText.Split('&');

            // 把切割后的字符串数组变成变量与数值组合的字典数组
            Dictionary<string, string> dicText = new Dictionary<string, string>();
            for (int i = 0; i < strSplitText.Length; i++)
            {
                // 获得第一个=字符的位置
                int nPos = strSplitText[i].IndexOf('=');

                // 获得字符串长度
                int nLen = strSplitText[i].Length;

                // 获得变量名
                string strKey = strSplitText[i].Substring(0, nPos);

                // 获得数值
                string strValue = strSplitText[i].Substring(nPos + 1, nLen - nPos - 1);

                // 放入字典类数组中
                dicText.Add(strKey, strValue);
            }
            try
            {

           
            if (dicText["res_data"] != null)
            {
                // token从res_data中解析出来（也就是说res_data中已经包含token的内容）
                var xmlDoc = new XmlDocument();
                try
                {
                    xmlDoc.LoadXml(dicText["res_data"]);
                    var selectSingleNode = xmlDoc.SelectSingleNode("/direct_trade_create_res/request_token");
                    if (selectSingleNode != null)
                    {
                        string strRequestToken = selectSingleNode.InnerText;
                        dicText.Add("request_token", strRequestToken);
                    }
                }
                catch (Exception exp)
                {
                    dicText.Add("request_token", exp.ToString());
                }
            }
            }
            catch (Exception)
            {

                throw new Exception(strText);
            }

            return dicText;
        }

        #endregion

        #region Methods

        /// <summary>
        /// 生成请求时的签名
        /// </summary>
        /// <param name="sPara">
        /// 请求给支付宝的参数数组
        /// </param>
        /// <returns>
        /// 签名结果
        /// </returns>
        private string BuildRequestMysign(Dictionary<string, string> sPara)
        {
            // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
            string prestr = AliCore.CreateLinkString(sPara);

            // 把最终的字符串签名，获得签名结果
            string mysign;
            switch (this.config.SignType)
            {
                case "MD5":
                    mysign = AliCore.Md5Sign(prestr, this.config.Key);
                    break;
                default:
                    mysign = string.Empty;
                    break;
            }

            return mysign;
        }

        /// <summary>
        /// 生成要请求给支付宝的参数数组
        /// </summary>
        /// <param name="sParaTemp">
        /// 请求前的参数数组
        /// </param>
        /// <returns>
        /// 要请求的参数数组
        /// </returns>
        private Dictionary<string, string> BuildRequestPara(Dictionary<string, string> sParaTemp)
        {
            // 待签名请求参数数组

            // 签名结果

            // 过滤签名参数数组 根据字母a到z的顺序把参数排序
            Dictionary<string, string> sPara = AliCore.FilterPara(sParaTemp);

            // 获得签名结果
            string mysign = this.BuildRequestMysign(sPara);

            // 签名结果与签名方式加入请求提交参数组中
            sPara.Add("sign", mysign);
            if (sPara["service"] != "alipay.wap.trade.create.direct"
                && sPara["service"] != "alipay.wap.auth.authAndExecute")
            {
                sPara.Add("sign_type", this.config.SignType);
            }

            return sPara;
        }

        /// <summary>
        /// 生成要请求给支付宝的参数数组
        /// </summary>
        /// <param name="sParaTemp">
        /// 请求前的参数数组
        /// </param>
        /// <param name="code">
        /// 字符编码
        /// </param>
        /// <returns>
        /// 要请求的参数数组字符串
        /// </returns>
        private string BuildRequestParaToString(Dictionary<string, string> sParaTemp, Encoding code)
        {
            // 待签名请求参数数组
            Dictionary<string, string> sPara;
            sPara = this.BuildRequestPara(sParaTemp);

            // 把参数组中所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串，并对参数值做urlencode
            string strRequestData = AliCore.CreateLinkStringUrlencode(sPara, code);

            return strRequestData;
        }

        #endregion
    }
}